/* vectors.S send all traps here. */
.global alltraps
alltraps:
    /*
     * Build your trap frame.
     * Hint:
     * 1. Use stp/ldp in favor of str/ldr to maintain stack alignment
     *    For example, `stp x1, x2, [sp, #-16]!` is equivalent to
     *    first `push x2` and then `push x1`. Be careful about the order.
     * 2. Use mrs/msr to move from/to system registers such as
     *    ELR_EL1, SPSR_EL1, and SP_EL0.
     */

    /* naïve tester */
    # sub x0,  x0,  x0
    # add x0,  x0,  #1
    # sub x30, x30, x30
    # add x30, x30, #2

    stp x29, x30, [sp, #-16]!
    stp x27, x28, [sp, #-16]!
    stp x25, x26, [sp, #-16]!
    stp x23, x24, [sp, #-16]!
    stp x21, x22, [sp, #-16]!
    stp x19, x20, [sp, #-16]!
    stp x17, x18, [sp, #-16]!
    stp x15, x16, [sp, #-16]!
    stp x13, x14, [sp, #-16]!
    stp x11, x12, [sp, #-16]!
    stp x9,  x10, [sp, #-16]!
    stp x7,  x8,  [sp, #-16]!
    stp x5,  x6,  [sp, #-16]!
    stp x3,  x4,  [sp, #-16]!
    stp x1,  x2,  [sp, #-16]!

    mrs x9,  SP_EL0     /* user stack pointer */
    mrs x10, SPSR_EL1   /* PSTATE */
    mrs x11, ELR_EL1    /* PC */
    mrs x12, TPIDR_EL0  /* TPIDR for musl */

    stp x9,  x0,  [sp, #-16]!
    stp x10, x11, [sp, #-16]!

    /*
     * Save Q0 and TPIDR_EL0 to placate musl such as
     * - `str q0, [sp, #-16]!`
     * - `mrs x12, tpidr_el0` and then push to stack
     */

    str q0, [sp, #-16]!
    stp xzr, x12, [sp, #-16]!  /* make sure sp is aligned on 16 bytes boundary */

    /*
     * Call trap(struct *trapframe).
     * Hint: The first argument is a stack pointer.
     */

    mov x0,  sp
    bl  trap

/* Return falls through to trapret. */
.global trapret
trapret:
    /*
     * Restore registers.
     * Hint: `ldp x1, x2, [sp], #16` is equivalent to first `pop x1`
     * and then `pop x2`.
     */

    /* Restore Q0 and TPIDR_EL0. */

    ldp x0,  x12, [sp], #16
    ldr q0, [sp], #16
    ldp x9,  x10, [sp], #16
    ldp x11, x0,  [sp], #16

    msr SPSR_EL1,  x9
    msr ELR_EL1,   x10
    msr SP_EL0,    x11
    msr TPIDR_EL0, x12

    ldp x1,  x2,  [sp], #16
    ldp x3,  x4,  [sp], #16
    ldp x5,  x6,  [sp], #16
    ldp x7,  x8,  [sp], #16
    ldp x9,  x10, [sp], #16
    ldp x11, x12, [sp], #16
    ldp x13, x14, [sp], #16
    ldp x15, x16, [sp], #16
    ldp x17, x18, [sp], #16
    ldp x19, x20, [sp], #16
    ldp x21, x22, [sp], #16
    ldp x23, x24, [sp], #16
    ldp x25, x26, [sp], #16
    ldp x27, x28, [sp], #16
    ldp x29, x30, [sp], #16

    eret

/* to avoid forkret + 8 magic */
.global _user_trapret
_user_trapret:
    bl forkret
    b trapret